#include "Comet.h"
#include "ThreadDS.h"
#include "Config.h"

const string Comet::MSGTYPE_RECV = "RECV";
const string Comet::MSGTYPE_SEND = "SEND";
const string Comet::MSGTYPE_DEL = "DEL";

int Comet::timeoutMillSec = 20000;

Comet::Comet(const string& cfgFile) {
	this->cfgFile = cfgFile;
	if(!initialize()) {
		cerr << "initialize error" << endl;
		return ;
	}
}

/**
 * get user, return null if user not exists
 */
User* Comet::getUser(const string& username) {
	if(users.count(username)) {
		return users[username];
	}
	return 0;
}

bool Comet::addUser(const string &username) {
	if(!getUser(username)) {
		User* user = new User();
		user->setUsername(username);
		user->setComet(this);
		users[username] = user;
		return true;
	}
	return false;
}

bool Comet::delUser(const string &username) {
	if(!getUser(username)) {
		return false;
	}
	delete users[username];
	users.erase(users.find(username));
	return true;
}

// private functions


/** 
 * initialize config parameters here
 */
bool Comet::initialize() {
	Config config;
	if(!config.read(cfgFile)) {
		return false;
	}

	port = config.getInt("port", 6000);
	blocks = config.getInt("blocks", 128);
	timeoutMillSec = config.getInt("timeout", Comet::getTimeoutMillSec());

	return true;
}

/**
 *	start socket listen
 */
bool Comet::startRunning() {
	cout << "try to start server" << endl;
	this->running = true;

	int err,sock_reuse=1;

#ifdef WIN32
	WORD wVersionRequested;
	WSADATA wsaData;
	wVersionRequested = MAKEWORD(1,1); //0x0101   
	err = WSAStartup(wVersionRequested,&wsaData);
	if(err!=0) {
		return false;
	}

	if(LOBYTE(wsaData.wVersion)!=1 || HIBYTE(wsaData.wVersion)!=1) {
		WSACleanup();
		return false;
	}
#endif

	sockSvr = socket(AF_INET, SOCK_STREAM, 0);
	err=setsockopt(sockSvr,SOL_SOCKET,SO_REUSEADDR,(char *)&sock_reuse,sizeof(sock_reuse));

	sockaddr_in addrSvr;
#ifdef WIN32
	addrSvr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
#else
	addrSvr.sin_addr.s_addr = INADDR_ANY;
#endif
	addrSvr.sin_family = AF_INET;
	addrSvr.sin_port = htons(this->port);
	if(bind(sockSvr, (SOCKADDR*)&addrSvr, sizeof(SOCKADDR)) == -1) {
		cerr << "bind " << port << " error" << endl;
		exit(1);
	}
	if(listen(sockSvr, this->blocks) == -1) {
		cerr << "listen error" << endl;
		exit(1);
	}
	SOCKADDR_IN addrClient;
	SOCKLEN_T len = sizeof(sockaddr);

	while(true) {
        fd = accept(sockSvr,(sockaddr*)&addrClient,&len);
        if(fd == -1) {
        	cerr << "accept error" << endl;
        }

		// start a new thread to handle this connection
		DWORD tid;
		THREADFUNC func = (THREADFUNC)handleClient;
		ThreadDS* args = new ThreadDS(this, fd);
#ifdef WIN32
		HANDLE handle = CreateThread(0, 0, func, args, CREATE_SUSPENDED, &tid);
		ResumeThread(handle);
#else
		pthread_create(&tid, NULL, func, args);
#endif
	}

	// never access here
#ifdef WIN32
	WSACleanup();
#else
	close(sockSvr);
#endif
	return true;
}

Comet::~Comet() {
	// remove all users
	for(map<string, User*>::iterator it = users.begin();
		it != users.end(); ++it) {
			delete it->second;
	}

	// delete socket fd
	cout << "try to remove" << endl;
#ifdef WIN32
	closesocket(sockSvr);
	WSACleanup();
#else
	cout << "close sockSvr: " << close(sockSvr) << endl;
#endif

}
